<template>
  <Teleport to="body">
    <transition name="zoom-fade" mode="out-in">
      <div :class="getClass" @click.stop v-if="visible">
        <div :class="`${prefixCls}-content`" v-click-outside="handleClose">
          <div :class="`${prefixCls}-input__wrapper`">
            <a-input :class="`${prefixCls}-input`" :placeholder="t('common.searchText')" ref="inputRef" allow-clear
              @change="handleSearch">
              <template #prefix>
                <SearchOutlined />
              </template>
            </a-input>
            <span :class="`${prefixCls}-cancel`" @click="handleClose">
              {{ t('common.cancelText') }}
            </span>
          </div>

          <div :class="`${prefixCls}-not-data`" v-show="getIsNotData">
            {{ t('component.app.searchNotData') }}
          </div>

          <ul :class="`${prefixCls}-list`" v-show="!getIsNotData" ref="scrollWrap">
            <li :ref="setRefs(index)" v-for="(item, index) in searchResult" :key="item.path" :data-index="index"
              @mouseenter="handleMouseenter" @click="handleEnter" :class="[
        `${prefixCls}-list__item`,
        {
          [`${prefixCls}-list__item--active`]: activeIndex === index,
        },
      ]">
              <div :class="`${prefixCls}-list__item-icon`">
                <Icon :icon="item.icon || 'mdi:form-select'" :size="20" />
              </div>
              <div :class="`${prefixCls}-list__item-text`">
                <!-- 搜索结果包含的字符着色 -->
                <span v-for="(each, i) in item.chars" :key="i" :class="{ highlight: each.highlight }">
                  {{ each.char }}
                </span>
              </div>
              <div :class="`${prefixCls}-list__item-enter`">
                <Icon icon="ant-design:enter-outlined" :size="20" />
              </div>
            </li>
          </ul>
          <AppSearchFooter />
        </div>
      </div>
    </transition>
  </Teleport>
</template>

<script lang="ts" setup>
import { computed, unref, ref, watch, nextTick } from 'vue';
import { SearchOutlined } from '@ant-design/icons-vue';
import AppSearchFooter from './AppSearchFooter.vue';
import Icon from '@/components/Icon/Icon.vue';
import vClickOutside from '@/directives/clickOutside';
import { useDesign } from '@/hooks/web/useDesign';
import { useRefs } from '@vben/hooks';
import { useMenuSearch } from './useMenuSearch';
import { useI18n } from '@/hooks/web/useI18n';
import { useAppInject } from '@/hooks/web/useAppInject';

const props = defineProps({
  visible: { type: Boolean },
});

const emit = defineEmits(['close']);

const scrollWrap = ref(null);
const inputRef = ref<HTMLElement | null>(null);

const { t } = useI18n();
const { prefixCls } = useDesign('app-search-modal');
const { refs, setRefs } = useRefs();
const { getIsMobile } = useAppInject();

const { handleSearch, searchResult, keyword, activeIndex, handleEnter, handleMouseenter } =
  useMenuSearch(refs, scrollWrap, emit);

const getIsNotData = computed(() => !keyword || unref(searchResult).length === 0);

const getClass = computed(() => {
  return [
    prefixCls,
    {
      [`${prefixCls}--mobile`]: unref(getIsMobile),
    },
  ];
});

watch(
  () => props.visible,
  (visible: boolean) => {
    visible &&
      nextTick(() => {
        unref(inputRef)?.focus();
      });
  },
);

function handleClose() {
  searchResult.value = [];
  emit('close');
}
</script>
<style lang="less" scoped>
@prefix-cls: ~'@{namespace}-app-search-modal';
@footer-prefix-cls: ~'@{namespace}-app-search-footer';

.@{prefix-cls} {
  display: flex;
  position: fixed;
  z-index: 800;
  top: 0;
  left: 0;
  justify-content: center;
  width: 100%;
  height: 100%;
  padding-top: 50px;
  background-color: rgb(0 0 0 / 25%);

  &--mobile {
    padding: 0;

    >div {
      width: 100%;
    }

    .@{prefix-cls}-input {
      width: calc(100% - 38px);
    }

    .@{prefix-cls}-cancel {
      display: inline-block;
    }

    .@{prefix-cls}-content {
      width: 100%;
      height: 100%;
      border-radius: 0;
    }

    .@{footer-prefix-cls} {
      display: none;
    }

    .@{prefix-cls}-list {
      height: calc(100% - 80px);
      max-height: unset;

      &__item {
        &-enter {
          opacity: 0 !important;
        }
      }
    }
  }

  &-content {
    position: relative;
    flex-direction: column;
    width: 632px;
    margin: 0 auto auto;
    border-radius: 16px;
    background-color: @component-background;
    box-shadow: 0 25px 50px -12px rgb(0 0 0 / 25%);
  }

  &-input__wrapper {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 14px 14px 0;
  }

  &-input {
    width: 100%;
    height: 48px;
    border-radius: 6px;
    color: #1c1e21;
    font-size: 1.5em;

    span[role='img'] {
      color: #999;
    }
  }

  &-cancel {
    display: none;
    color: #666;
    font-size: 1em;
  }

  &-not-data {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: 100px;
    color: rgb(150 159 175);
    font-size: 0.9;
  }

  &-list {
    max-height: 472px;
    margin: 0 auto;
    margin-top: 14px;
    padding: 0 14px;
    padding-bottom: 20px;
    overflow: auto;

    &__item {
      display: flex;
      position: relative;
      align-items: center;
      width: 100%;
      height: 56px;
      margin-top: 8px;
      padding-bottom: 4px;
      padding-left: 14px;
      border-radius: 4px;
      background-color: @component-background;
      box-shadow: 0 1px 3px 0 #d4d9e1;
      color: @text-color-base;
      font-size: 14px;
      cursor: pointer;

      >div:first-child,
      >div:last-child {
        display: flex;
        align-items: center;
      }

      &--active {
        background-color: @primary-color;
        color: #fff;

        .@{prefix-cls}-list__item-enter {
          opacity: 1;
        }
      }

      &-icon {
        width: 30px;
      }

      &-text {
        flex: 1;

        // 搜索结果包含的字符着色
        &>span {
          &.highlight {
            color: lighten(@primary-color, 20%);
          }
        }
      }

      &-enter {
        width: 30px;
        opacity: 0;
      }
    }
  }
}
</style>
